﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Net;
using System.Threading.Tasks;
using Microsoft.AspNetCore.Http;
using Microsoft.AspNetCore.Mvc;
using Microsoft.EntityFrameworkCore;
using Microsoft.Extensions.Logging;
using MyEventClient.Event;
using MyEventClient.Infrastructure;
using MyEventClient.Service;

namespace MyEventClient.Controllers
{
    [Route("api/[controller]")]
    [ApiController]
    public class BooksController : ControllerBase
    {
        private readonly IBookEventService bookEventService;
        private readonly ILogger<BookEventService> logger;
        private readonly BookContext bookContext;

        public BooksController(IBookEventService bookEventService, ILogger<BookEventService> logger, BookContext bookContext)
        {
            this.bookEventService = bookEventService ?? throw new ArgumentNullException(nameof(bookEventService));
            this.logger = logger ?? throw new ArgumentNullException(nameof(logger));
            this.bookContext = bookContext;
            
        }

        [HttpGet]
        [Route("items/{id:int}")]
        [ProducesResponseType((int)HttpStatusCode.NotFound)]
        [ProducesResponseType((int)HttpStatusCode.BadRequest)]
        [ProducesResponseType(typeof(Book), (int)HttpStatusCode.OK)]
        public async Task<ActionResult<Book>> ItemByIdAsync(int id)
        {
            if (id <= 0)
            {
                return BadRequest();
            }

            var item = await bookContext.Books.SingleOrDefaultAsync(b => b.BookId == id);

            if (item != null)
            {
                return item;
            }

            return NotFound();
        }

        //POST api/v1/[controller]/items
        [Route("items")]
        [HttpPost]
        [ProducesResponseType((int)HttpStatusCode.Created)]
        public async Task<ActionResult<Book>> CreateProductAsync([FromBody] Book b)
        {
            var item = new Book
            {
                BookId = b.BookId,
                Title = b.Title
            };

            bookContext.Books.Add(item);

            await bookContext.SaveChangesAsync();

            //return CreatedAtAction(nameof(ItemByIdAsync), new { id = item.BookId }, null);
            return item;
        }

        //PUT api/v1/[controller]/items
        [Route("items")]
        [HttpPut]
        [ProducesResponseType((int)HttpStatusCode.NotFound)]
        [ProducesResponseType((int)HttpStatusCode.Created)]
        public async Task<ActionResult> UpdateProductAsync([FromBody] Book bookToUpdate)
        {
            var bookItem = await bookContext.Books.SingleOrDefaultAsync(i => i.BookId == bookToUpdate.BookId);

            if (bookItem == null)
            {
                return NotFound(new { Message = $"Item with id {bookToUpdate.BookId} not found." });
            }

            var oldTitle = bookItem.Title;
            var raiseBookTitleChangedEvent = oldTitle != bookToUpdate.Title;

            // Update current book
            bookItem.Title = bookToUpdate.Title;
            bookContext.Books.Update(bookItem);

            if (raiseBookTitleChangedEvent) // Save book's data and publish integration event through the Event Bus if title has changed
            {
                //Create Integration Event to be published through the Event Bus
                var bookTitleChangedEvent = new BookTitleChangeEvent(bookItem.BookId, bookItem.Title, oldTitle);

                // Achieving atomicity between original book database operation and the IntegrationEventLog thanks to a local transaction
                await bookEventService.SaveEventAndChangesAsync(bookTitleChangedEvent);

                // Publish through the Event Bus and mark the saved event as published
                await bookEventService.PublishThroughEventBusAsync(bookTitleChangedEvent);
            }
            else // Just save the updated book because the book's title hasn't changed.
            {
                await bookContext.SaveChangesAsync();
            }

            return new JsonResult(bookItem);//CreatedAtAction(nameof(ItemByIdAsync), new { id = bookToUpdate.BookId }, null);
        }



    }
}
